
// sqlite、mysql、postgresql、oracle、mssql、mongodb、leveldb、redis
//  g++ testdb.cpp -lsqlite3
#include <iostream>

// libsqlite3-dev
#include <sqlite3.h>

int sqlite3_select_callback(
    void *data,         /* Data provided in the 4th argument of sqlite3_exec() */
    int columns,        /* The number of columns in row */
    char **field_names, /* An array of strings representing fields in the row */
    char **column_names /* An array of strings representing column names */
)
{
    for (int i = 0; i < columns; i++)
    {
        std::cout << *(column_names + i) << ": " << *(field_names + i) << std::endl;
    }

    return 0;
}

void sqlite()
{
    sqlite3 *db = nullptr;
    if (sqlite3_open("./sqlite.db", &db) != SQLITE_OK)
    {
        std::cout << "open:" << sqlite3_errmsg(db) << std::endl;
        return;
    }

    auto csql = "create table stest (name VARCHAR(25) primary key not null, sex BOOLEAN, age SMALLINT not null, uptime DATETIME)";
    char *err = nullptr;
    if (sqlite3_exec(db, csql, nullptr, nullptr, &err) != SQLITE_OK)
    {
        std::cout << "create:" << err << std::endl;
        sqlite3_free(err);
    }

    auto dsql = "insert into stest values ('dongle', true, 36, '2021-03-13 10:41:35')";
    if (sqlite3_exec(db, dsql, nullptr, nullptr, &err) != SQLITE_OK)
    {
        std::cout << "insert:" << err << std::endl;
        sqlite3_free(err);
    }

    auto qsql = "select * from stest";
    if (sqlite3_exec(db, qsql, sqlite3_select_callback, nullptr, &err) != SQLITE_OK)
    {
        std::cout << "select:" << err << std::endl;
        sqlite3_free(err);
    }

    char **res = nullptr;
    int res_row = 0;
    int res_col = 0;
    if (sqlite3_get_table(db, qsql, &res, &res_row, &res_col, &err) == SQLITE_OK)
    {
        for (int i = 0; i < res_row; i++)
        {
            for (int j = 0; j < res_col; j++)
            {
                std::cout << *(res + j) << ": " << *(res + (i + 1) * res_col + j) << std::endl;
            }
        }

        sqlite3_free_table(res);
    }
    else
    {
        std::cout << "select2:" << err << std::endl;
        sqlite3_free(err);
    }

    sqlite3_stmt *stmt = nullptr;
    if (sqlite3_prepare_v2(db, qsql, -1, &stmt, nullptr) == SQLITE_OK)
    {
        while (sqlite3_step(stmt) == SQLITE_ROW)
        {
            std::cout << sqlite3_column_name(stmt, 0) << ": " << sqlite3_column_text(stmt, 0) << std::endl;
            std::cout << sqlite3_column_name(stmt, 1) << ": " << sqlite3_column_int(stmt, 1) << std::endl;
            std::cout << sqlite3_column_name(stmt, 2) << ": " << sqlite3_column_int(stmt, 2) << std::endl;
            std::cout << sqlite3_column_name(stmt, 3) << ": " << sqlite3_column_text(stmt, 3) << std::endl;
        }
    }
    else
    {
        std::cout << "open:" << sqlite3_errmsg(db) << std::endl;
    }

    sqlite3_close(db);
}

// default-libmysqlclient-dev
#include <mariadb/mysql.h>

void mysql()
{
    MYSQL conn = {0};
    if (mysql_init(&conn) == nullptr)
        return;

    if (mysql_real_connect(&conn, "127.0.0.1", "root", "qwerty", "ghost_prod", 3306, nullptr, 0) == nullptr)
    {
        std::cout << mysql_error(&conn) << std::endl;
        return;
    }

    std::cout << mysql_get_server_name(&conn) << " " << mysql_get_server_info(&conn) << std::endl;

    auto sql = "select title,status,published_at from posts";
    if (mysql_query(&conn, sql) == 0)
    {
        auto res = mysql_store_result(&conn);
        auto cols = mysql_num_fields(res);
        while (auto row = mysql_fetch_row(res))
        {
            for (int i = 0; i < cols; i++)
                std::cout << row[i] << std::endl;
        }
        mysql_free_result(res);
    }
    else
    {
        std::cout << mysql_error(&conn) << std::endl;
    }

    mysql_close(&conn);
}

// libpqxx-dev
//#include <pqxx/pqxx>
void postgre()
{
}

//需要下载occi，instantclient-sdk-linux
//#include <occi.h>
void oracle()
{
}

// libmongoclient-dev
// g++ testdb.cpp -lsqlite3 -lmysqlclient -lmongoclient -lboost_system
#include <mongo/client/dbclient.h>
#include <mongo/bson/bson.h>

void mongodb()
{
    std::string errorStr;
    auto connStr = mongo::ConnectionString::parse("mongodb://test:12345678;@127.0.0.1:27017/invest", errorStr);
    if (!connStr.isValid())
    {
        std::cout << errorStr << std::endl;
        return;
    }

    auto client = connStr.connect(errorStr);
    auto names = client->getCollectionNames("invest");
    for (auto it = names.begin(); it != names.end(); it++)
    {
        std::cout << *it << std::endl;
    }

    auto obj = client->findOne("", mongo::Query(""));
    std::set<std::string> fields;
    if (obj.getFieldNames(fields) > 0)
    {
        for (auto it = fields.begin(); it != fields.end(); it++)
        {
            std::cout << *it << " " << obj.getField(*it).String() << std::endl;
        }
    }
}

// libleveldb-dev
// g++ testdb.cpp -lsqlite3 -lmysqlclient -lmongoclient -lboost_system -lleveldb
#include <leveldb/db.h>

void level()
{
    leveldb::Options opts;
    opts.create_if_missing = true;
    leveldb::DB *db;
    auto status = leveldb::DB::Open(opts, "./level.db", &db);
    if (!status.ok())
    {
        std::cout << status.ToString() << std::endl;
        return;
    }

    leveldb::WriteOptions wopts;
    status = db->Put(wopts, "name", "lidanger");
    if (!status.ok())
    {
        std::cout << status.ToString() << std::endl;
        return;
    }

    leveldb::ReadOptions ropts;
    std::string val;
    status = db->Get(ropts, "name", &val);
    if (!status.ok())
    {
        std::cout << status.ToString() << std::endl;
        return;
    }

    std::cout << "name：" << val << std::endl;
}

// librocksdb-dev
// g++ testdb.cpp -lsqlite3 -lmysqlclient -lmongoclient -lboost_system -lleveldb -lhiredis -lrocksdb

#include "rocksdb/db.h"
#include "rocksdb/slice.h"
#include "rocksdb/options.h"
#include "rocksdb/utilities/transaction.h"
#include "rocksdb/utilities/transaction_db.h"
#include <cassert>

std::string kDbPath = "/tmp/wsf_rocksdb_test";
std::string kDbPathTran = "/tmp/wsf_rocksdb_transation";

void testTransaction()
{
    rocksdb::TransactionDB *txn_db;
    rocksdb::Options options;
    options.create_if_missing = true;
    rocksdb::TransactionDBOptions txn_db_options;

    rocksdb::Status s = rocksdb::TransactionDB::Open(options, txn_db_options, kDbPathTran, &txn_db);
    auto txn = txn_db->BeginTransaction(rocksdb::WriteOptions());
    s = txn->Put("key", "value");
    s = txn->Delete("key2");
    s = txn->Merge("key3", "value");
    s = txn->Commit();
    delete txn;
}

void test_rocksdb()
{
    rocksdb::DB *db;
    rocksdb::Options options;
    options.create_if_missing = true;

    rocksdb::Status s = rocksdb::DB::Open(options, kDbPath, &db);
    assert(s.ok());

    s = db->Put(rocksdb::WriteOptions(), "k1", "v1");
    assert(s.ok());

    std::string v;
    s = db->Get(rocksdb::ReadOptions(), "k1", &v);
    assert(s.ok());
    std::cout << "v = " << v << std::endl;

    // 批量
    rocksdb::WriteBatch batch;
    batch.Delete("k1");
    batch.Put("k2", "v2");
    batch.Put("k3", "v3");
    batch.Put("k4", "v4");
    s = db->Write(rocksdb::WriteOptions(), &batch);
    if (s.ok())
    {
        std::string v2;
        s = db->Get(rocksdb::ReadOptions(), "k2", &v2);
        std::cout << "v = " << v2 << std::endl;
        s = db->Get(rocksdb::ReadOptions(), "k1", &v);
        if (!s.ok())
        {
            std::cout << s.ToString() << std::endl;
        }
        // assert(s.ok());
        // std::cout<<"v = "<<v<<std::endl;
    }

    // 遍历
    rocksdb::Iterator *it = db->NewIterator(rocksdb::ReadOptions());
    for (it->SeekToFirst(); it->Valid(); it->Next())
    {
        std::cout << it->key().ToString() << ":" << it->value().ToString() << std::endl;
    }

    // 事务
    testTransaction();
}

// libhiredis-dev
// g++ testdb.cpp -lsqlite3 -lmysqlclient -lmongoclient -lboost_system -lleveldb -lhiredis
#include <hiredis/hiredis.h>

void redis()
{
    auto context = redisConnect("127.0.0.1", 6379);
    if (context == nullptr || context->err != REDIS_OK)
    {
        std::cout << context->errstr << std::endl;
        return;
    }

    redisReply *reply = nullptr;
    redisAppendCommand(context, "set test iowenf");
    redisAppendCommand(context, "lpush test1 sfasdf");
    redisAppendCommand(context, "lpush test1 fdsgewe");
    redisAppendCommand(context, "lpush test1 fsfweevvsd");
    redisGetReply(context, (void **)&reply);
    if (reply == nullptr)
    {
        std::cout << context->errstr << std::endl;
    }
    else
    {
        if (reply->type == REDIS_REPLY_STATUS)
        {
            std::cout << reply->str << std::endl;
        }
        else
            while (redisGetReply(context, (void **)&reply) == REDIS_OK)
                if (reply->type == REDIS_REPLY_STATUS)
                {
                    std::cout << reply->str << std::endl;
                    break;
                }
    }

    reply = (redisReply *)redisCommand(context, "get test");
    if (reply == nullptr)
    {
        std::cout << context->errstr << std::endl;
    }
    else
    {
        if (reply->type == REDIS_REPLY_STRING)
            std::cout << reply->str << std::endl;
        else
            while (redisGetReply(context, (void **)&reply) == REDIS_OK)
                if (reply->type == REDIS_REPLY_STRING)
                {
                    std::cout << reply->str << std::endl;
                    break;
                }
    }

    reply = (redisReply *)redisCommand(context, "rpop test1");
    if (reply == nullptr)
    {
        std::cout << context->errstr << std::endl;
    }
    else
    {
        if (reply->type == REDIS_REPLY_STRING)
        {
            std::cout << reply->str << std::endl;
        }
        else
            while (redisGetReply(context, (void **)&reply) == REDIS_OK)
                if (reply->type == REDIS_REPLY_STRING)
                {
                    std::cout << reply->str << std::endl;
                    break;
                }
    }

    reply = (redisReply *)redisCommand(context, "lrange test1 0 9");
    if (reply == nullptr)
    {
        std::cout << context->errstr << std::endl;
    }
    else
    {
        if (reply->type == REDIS_REPLY_ARRAY)
        {
            for (int i = 0; i < reply->elements; i++)
            {
                auto ele = *(reply->element + i);
                if (ele->type == REDIS_REPLY_STRING)
                    std::cout << ele->str << std::endl;
            }
        }
        else
            while (redisGetReply(context, (void **)&reply) == REDIS_OK)
                if (reply->type == REDIS_REPLY_ARRAY)
                {
                    for (int i = 0; i < reply->elements; i++)
                    {
                        auto ele = *(reply->element + i);
                        if (ele->type == REDIS_REPLY_STRING)
                            std::cout << ele->str << std::endl;
                    }
                    break;
                }
    }

    redisFree(context);
}

int main()
{
    // sqlite();
    // mysql();
    postgre();
    oracle();
    // mongodb();
    // level();
    test_rocksdb();
    // redis();

    return 0;
}
